﻿#ifndef _E5295BC9_8074_44E9_A754_2B8ADCCB3004
#define _E5295BC9_8074_44E9_A754_2B8ADCCB3004

// 数据结构：BaseChannel, TranPack
// 收发逻辑：Transceiver

#include "depends/incfile.h"
#include "CFunc.h"
#include "xCores.h"
#include "SysUtil.h"
#include "CrypUtil.h"

namespace x2lib
{

    /*************************************************************************
    ** Desc		: 通用数据通道，用户通过继承实现ApiSend/ApiRecv接口，可实现多种方式的数据收发，如Socket(Tcp/udp),Pipe等，
    **              不必关心数据发送时的数据格式，数据大小问题，也不必关心接收时数据的完整性，丢包坏包问题，
    **              另外通过Listener::OnEnSend可以在发送前一刻进行自定义加密，在Listener::OnDeRecv在接收最初进行自定义解密。
    **              其他特点：提供了丰富的事件机制，同步异步接收机制等。
    **              发送时使用队列缓冲，接收时通过用户指定内存缓冲。
    **              注意：整体架构已完成，细节还在完善中 xktesla@2020-12-18 09:11:26
    ** Author	: xktesla@2017-7-29 17:50:22
    *************************************************************************/
    class Transceiver
    {
    public:
        /*************************************************************************
        ** Desc		: 拆分包结构。一般不超过64k，自带nSrcID/nDstID的好处是，当系统包含一个中转服务时，中转服务程序可以不用等待得
        **              到完整包再开始转发。（转发逻辑需要用户实现，DataChannel并不因nSrcID/nDstID自动改变数据流向）；
        **				nByID0/nByID1可以增强数据包传输能力，比如可用于实现类似CDN的业务；
        ** Author	: xktesla@2017-7-29 17:50:22
        *************************************************************************/
        struct TranPack
        {
            TranPack()
            {
                reset();
            }
            struct tagHead
            {
                uint8_t mgCode[16]; // 拆分包的起始标记
                uint32_t nMsgId; // 消息id，一组TranPack共享同一个id
                uint32_t iNodeI; // 指示当前节点的索引[0~4]
                uint32_t iNodeX[4]; // 可以用来保存数据链路中的节点id，例如可将X[0]设为nSrcID，X[3]设为nDstID，X[1],X[2]设为转发节点ID
                uint32_t iSeek; // 当前TranPack::Body在完整包中的起始偏移
                uint32_t nBytes; // 拆分包总大小Body
                uint32_t nTotal; // 完整包的大小（用户调用PushBack时传入的nData，同时也等于所有拆分包Body的总和）
                uint32_t uCrc32; // 当前包Body的crc32
            }Head;
            char Body; // 取地址即可得到pData的地址
            void reset(const uint8_t mgCode[16] = nullptr)
            {
                memset(this, 0, sizeof(Head));
                if (mgCode != nullptr) { memcpy(Head.mgCode, mgCode, sizeof(Head.mgCode)); }
            }
        
            void setPack(uint32_t nMsgId, uint32_t iNodeI, const uint32_t iNodeX[4], uint32_t iSeek, uint32_t nTotal, uint8_t *pData, uint16_t nBytes)
            {
                this->Head.nMsgId = nMsgId;
                this->Head.iNodeI = iNodeI;
                memset(this->Head.iNodeX, 0, sizeof(this->Head.iNodeX));
                if (iNodeX)
                {
                    memcpy(this->Head.iNodeX, iNodeX, sizeof(this->Head.iNodeX));
                }
                this->Head.iSeek = iSeek;
                this->Head.nBytes = nBytes;
                this->Head.nTotal = nTotal;
                this->Head.uCrc32 = CrypUtil::CalcCrc32(pData, nBytes);
                memcpy(&this->Body, pData, nBytes);
            }

            void* memAddr()
            {
                return this;
            }

            uint32_t memSize()
            {
                return MIN_SIZE + this->Head.nBytes;
            }

            uint8_t* bodyAddr()
            {
                return (uint8_t*)&this->Body;
            }
            uint32_t bodySize()
            {
                return this->Head.nBytes;
            }

            bool isValid()
            {
                if(this->Head.uCrc32 == 0) { return false ;}
                if (this->Head.nBytes > (this->Head.nTotal) || this->Head.iSeek + this->Head.nBytes != this->Head.nTotal) { return false; }
                return (this->Head.uCrc32 != CrypUtil::CalcCrc32(&this->Body, this->Head.nBytes));
            }

            static const uint8_t MIN_SIZE = sizeof(tagHead); // 最小长度：包头的大小（Head）
            // static const uint32_t MAX_SIZE = 1024*1024*32; // 最大长度
        };

        struct AbsPot
        {
            AbsPot() 
            {
                static uint32_t s_id_counter = 0;
                id = ++s_id_counter;
                uuid[0] = 0;
            }
            virtual ~AbsPot()
            {
                
            }
            uint32_t id; // 同步到DataChannel::TranPack::nSrcID/nDstID
            char uuid[64]; // 全球唯一，可以用来作为身份查找，权限验证等
        };

        struct AbsChannel
        {
            friend class Transceiver;

            struct Buffer
            {
                Buffer(uint16_t size)
                {
                    pData = (uint8_t*)calloc(1, size);
                    nData = size;
                    beginIndex = 0;
                    endIndex = 0;
                }
                virtual ~Buffer()
                {
                    free(pData);
                }

                uint8_t& operator[](uint16_t index) 
                {
                    return pData[index];
                }

                uint32_t totalBytes()
                {
                    return nData;
                }
                    
                uint32_t filledBytes()
                {
                    return (endIndex - beginIndex + totalBytes())%totalBytes();
                }
                    
                uint32_t spaceBytes()
                {
                    return totalBytes() - filledBytes();
                }

                uint8_t* pData;
                uint32_t nData;
                uint32_t beginIndex;
                uint32_t endIndex;
            };


            AbsChannel(uint16_t packSize = 1024 * 4) : 
                uPackSize(packSize), handle(0), auth(0), 
                beat(60 * 10), tick(0), tmin(0), tmout(0), conn(false), reco(0), 
                local(nullptr)//, other(nullptr) 
            {
                pPackTxed = (TranPack*)malloc(uPackSize);
                pPackRxed = (TranPack*)malloc(uPackSize);
                pPackTxBuffer = new Buffer(uPackSize * 8);
                pPackRxBuffer = new Buffer(uPackSize * 8);
            }
            virtual ~AbsChannel()
            {
                free(pPackTxed);
                free(pPackRxed);
                free(pPackTxBuffer);
                free(pPackRxBuffer);
            }

            intz_t handle;
            uint8_t auth; // 验证状态
            uint32_t beat; // 心跳间隔，单位秒
            uint32_t tick; // 最近一次收发数据的tick
            uint32_t tmin; // 建立时间戳
            uint32_t tmout; // 断开时间戳
            bool conn; // 是否连接
            uint8_t reco; // 断线重连次数，重连时间间隔使用心跳时间间隔beat；
            // 预留3个用户指针
            //void* local; // 本端描述体指针
            //void* other; // 远端描述体指针
            // template<class T> T local(){ return static_cast<T>(this->_local); }
            // template<class T> T other(){ return static_cast<T>(this->_other); }
            AbsPot* local;
            AbsPot other;

            TranPack* pPackTxed; // Listener::OnPackSended
            TranPack* pPackRxed; // Listener::OnPackRecved, by user set

        private:
            uint16_t uPackSize;

            Buffer *pPackTxBuffer; // ring queue
            Buffer *pPackRxBuffer; // ring queue
        };

        
    public:
        Transceiver(bool isClient, uint8_t mgCode[16], int nPackSize = 1024 * 32);
        virtual ~Transceiver();
        bool Init();
        // uRetry重试次数（间隔15s），仅当作为Client时有效
        bool Execute(int reConnWaitsec = 15, int reConnTimes = 10);
        void UnInit();
        AbsChannel* FindChannel(std::function<bool(const AbsChannel*)> condition);

    protected:
        virtual AbsChannel* connectServer(bool isReConn) = 0;
        virtual AbsChannel* prepareServer() = 0;
        virtual AbsChannel* acceptClients() = 0;
        virtual void closeChannel(AbsChannel* pChannel) = 0;
        virtual void keepAlive(AbsChannel* pChannel) {};

        virtual void OnConnected(AbsChannel* pChannel) = 0;

        /*************************************************************************
        ** Desc		: 发送前的原始数据。用户自定义加密算法（仅支持逐字节加密）
        **      	: [in] pData
        **          : [in] nData
        ** Return	:
        ** Author	: xktesla@2017-8-21 17:35:00
        *************************************************************************/
        virtual void OnBytesEnSend(uint8_t* pData, uint32_t nData)
        {
            CrypUtil::Encrypt(pData, pData, nData, WEAK_KEY);
        }
        virtual void OnPackEnSend(AbsChannel* pChannel)
        {
            //uint8_t* pData = (uint8_t*)pChannel->pPackTxed;
            //uint32_t nData = TranPack::MIN_SIZE + pChannel->pPackTxed->Head.nBytes;
            //CrypUtil::Encrypt(pData, pData, nData, WEAK_KEY);
        };

        /*************************************************************************
        ** Desc		: 接收后的原始数据。用户自定义解密算法（仅支持逐字节加密）
        **      	: [in] pData
        **          : [in] nData
        ** Return	:
        ** Author	: xktesla@2017-8-21 17:35:00
        *************************************************************************/
        virtual void OnBytesDeRecv(uint8_t* pData, uint32_t nData)
        {
            CrypUtil::Decrypt(pData, pData, nData, WEAK_KEY);
        }
        virtual void OnPackDeRecv(AbsChannel* pChannel)
        {
            //uint8_t* pData = (uint8_t*)pChannel->pPackRxed;
            //uint32_t nData = TranPack::MIN_SIZE + pChannel->pPackRxed->Head.nBytes;
            //CrypUtil::Decrypt(pData, pData, nData, WEAK_KEY);
        };

        virtual void OnPackSended(AbsChannel* pChannel);

        virtual void OnPackRecved(AbsChannel* pChannel);

        virtual void OnProcessing(AbsChannel* pChannel, uint32_t dealt, uint32_t total) {}
        
    public:
        /*************************************************************************
        ** Desc		: 向缓冲区写入要发送的数据，频繁失败意味着缓冲区已满
        ** Param	: [in] iNodeI
        **			: [in] iNodeX[4]
        **			: [in] pData
        **			: [in] nData 要发送的数据大小
        **			: [in] isWait 缓冲区满的情况下是否进行循环等待（50ms）重试
        ** Return	: 是否发送成功
        ** Author	: xktesla@2017-8-21 17:35:00
        *************************************************************************/
        bool PostData(AbsChannel* pChannel, const void* pData, uint32_t nData, uint32_t nMsgId = 0, uint32_t iNodeI = 0, const uint32_t iNodeX[4] = nullptr, bool isWait = true);
        bool SendData(AbsChannel* pChannel, const void* pData, uint32_t nData, uint32_t nMsgId = 0, uint32_t iNodeI = 0, const uint32_t iNodeX[4] = nullptr, bool isWait = true);

        bool pushData(uint8_t* pData, uint32_t nData, AbsChannel* pChannel, bool isTx, bool isWait);
        TranPack* pullData(AbsChannel* pChannel, bool isTx, bool isWait);

    protected:
        /*************************************************************************
        ** Desc		: 发送接口，推荐以异步方式实现。比如可以用Socket[send/recv]，Pipe[WriteFile/ReadFile]等实现
        ** Param	: [in] vData 由TrySend传入的参数
        **            [in] pData
        **			  [in] nData
        **			  [in] pnBytes
        ** Return	: 0-断开连接，1-连接正常，2-缓冲区满
        ** Author	: xktesla@2017-8-21 11:54:36
        *************************************************************************/
        virtual int ApiSend(AbsChannel* pChannel, void* pData, uint32_t nData, uint32_t* pnBytes) = 0;

        /*************************************************************************
        ** Desc		: 接收接口，推荐以异步方式实现。比如可以用Socket[send/recv]，Pipe[WriteFile/ReadFile]等实现
        ** Param	: [in] vData 由TryRecv传入的参数
        **			  [in] pData
        **			  [in] nData
        **			  [in] pnBytes
        ** Return	: true-连接正常，false-连接断开
        ** Author	: xktesla@2017-8-21 11:54:36
        *************************************************************************/
        virtual bool ApiRecv(AbsChannel* pChannel, void* pData, uint32_t nData, uint32_t* pnBytes) = 0;


        bool m_isClient;
        AbsChannel* m_pMyChannel;

    private:
        /*************************************************************************
        ** Desc		: 应放在循环中以时间片的方式轮询接收
        **			: [in] ulWaitms 等待时长，设置为-1时会完全阻塞，直到有新数据进来（无视连接状态）
        ** Return	: 0-断开连接，1-连接正常且缓冲到数据，2-连接正常但未缓冲到数据
        ** Author	: xktesla@2017-8-21 17:35:00
        *************************************************************************/
        int recvData(AbsChannel* pChannel, const uint32_t uWaitms = 0);

        /*************************************************************************
        ** Desc		: 调用ApiSend发送数据
        ** Param	: [in] vData 会直接传给ApiSend
        ** Return	: 是否发送成功
        ** Author	: xktesla@2017-8-21 17:35:00
        *************************************************************************/
        // 发送顺序为：只从QUE取数据发送，当得到一个QUE_FREE后，由PushBack向QUE_FREE添加数据
        int trySend(AbsChannel* pChannel, bool isWait);

        /*************************************************************************
        ** Desc		: 调用ApiRecv接收数据
        ** Param	: [in] vData 会直接传给ApiRecv
        ** Return	: 0-断开连接，1-连接正常且缓冲到一个完整的拆分包，2-连接正常但未缓冲到数据，3-连接正常的其他情况
        ** Author	: xktesla@2017-8-21 17:35:18
        *************************************************************************/
        int tryRecv(AbsChannel* pChannel);

        bool m_isRunning;
        static const unsigned char WEAK_KEY = 0x12;
        std::vector<AbsChannel*> m_vecpChannel;
        xCores::Mutex m_mtxChannel;
        uint8_t m_mgCode[16];

    };
}

#endif
